from types import GeneratorType


def _escape(text):
    out = []
    for ch in text:
        if ord(ch) < 32 or ord(ch) > 127:
            out.append("\\u%04x" % (ord(ch),))
        elif ch == "\\":
            out.append("\\\\")
        elif ch == '"':
            out.append('\\"')
        elif ch == "/":
            out.append("\\/")
        elif ch == "\b":
            out.append("\\b")
        elif ch == "\f":
            out.append("\\f")
        elif ch == "\t":
            out.append("\\t")
        elif ch == "\r":
            out.append("\\r")
        elif ch == "\n":
            out.append("\n")
        else:
            out.append(ch)
    return "".join(out)

def _unescape(ch):
    if ch == "n":
        return "\n"
    elif ch == "\\":
        return "\\"
    elif ch == '"':
        return '"'
    elif ch == "t":
        return "\t"
    elif ch == "r":
        return "\r"
    elif ch == "b":
        return "\b"
    elif ch == "/":
        return "/"
    elif ch == "u":
        val = int(data.next() + data.next() + data.next() + data.next(), 16) 
        return unichr(val)
    else:
        raise ValueError("invalid escape sequence '\\%c'" % (ch,))

def dump(obj):
    if obj is None:
        return "null"
    elif isinstance(obj, bool):
        if obj:
            return "true"
        else:
            return "false"
    elif isinstance(obj, (int, long, float)):
        return str(obj)
    elif isinstance(obj, basestring):
        return '"' + _escape(obj) + '"'
    elif isinstance(obj, (list, tuple, GeneratorType)):
        elems = [dump(e) for e in obj]
        return "[" + ",".join(elems) + "]"
    elif isinstance(obj, dict):
        elems = ["%s:%s" % (dump(k), dump(v)) for k, v in obj.iteritems()]
        return "{" + ",".join(elems) + "}"
    elif hasattr(obj, "to_json"):
        return dump(obj.to_json())
    else:
        raise TypeError("can't encode %r" % (obj,))


_unread = None

def _load(data):
    global _unread
    if _unread:
        ch = _unread
        _unread = None
    else:
        ch = data.next()
    while ch in " \t\r\n":
        ch = data.next()
    if ch == '"':
        out = []
        while True:
            ch = data.next()
            if ch == "\\":
                out.append(_unescape(data.next()))
            elif ch == '"':
                break
            else:
                out.append(ch)
        return "".join(out)
    elif ch in "tfn":
        kwd = ch + data.next() + data.next() + data.next()
        if kwd == "null":
            return None
        elif kwd == "true":
            return True
        elif kwd == "fals" and data.next() == "e":
            return False
    elif ch in "0123456789+-":
        digits = [ch]
        ch = data.next()
        while ch in "0123456789":
            digits.append(ch)
            ch = data.next()
        if ch == ".":
            digits.append(".")
            ch = data.next()
            while ch in "0123456789":
                digits.append(ch)
                ch = data.next()
            if ch == "e" or ch == "E":
                digits.append("e")
                ch = data.next()
                while ch in "+-0123456789":
                    digits.append(ch)
                    ch = data.next()
            _unread = ch
            return float("".join(digits))
        else:
            _unread = ch
            return int("".join(digits))
    elif ch == "[":
        out = []
        while True:
            out.append(_load(data))
            if _unread:
                ch = _unread
                _unread = None
            else:
                ch = data.next()
            if ch in ", \t\r\n":
                pass
            elif ch == "]":
                break
            else:
                raise ValueError("invalid syntax, expected ',' or ']', found %r" % (ch,))
        return out
    elif ch == "{":
        out = {}
        while True:
            k = _load(data)
            ch = data.next()
            if ch != ":":
                raise ValueError("expected ':', found %r" % (ch,))
            out[k] = _load(data)
            if _unread:
                ch = _unread
                _unread = None
            else:
                ch = data.next()
            if ch in ", \t\r\n":
                pass
            elif ch == "}":
                break
            else:
                raise ValueError("invalid syntax, expected ',' or '}', found %r" % (ch,))
        return out
    else:
        raise ValueError("invalid syntax: %r" % (ch,))


def load(data):
    try:
        return _load(iter(data.strip()))
    except StopIteration:
        raise ValueError("unterminated data %r" % (data,))


if __name__ == "__main__":
    x = dump({"x":[1,23,"hello",456.789,"world"], "y":17})
    print x
    print load(x)
















